package com.example.smartwaterapi.scheduler;

import com.example.smartwaterapi.entity.Alarm;
import com.example.smartwaterapi.entity.User;
import com.example.smartwaterapi.entity.Device;
import com.example.smartwaterapi.service.AlarmService;
import com.example.smartwaterapi.service.TemplateMessageService;
import com.example.smartwaterapi.service.UserService;
import com.example.smartwaterapi.service.DeviceService;
import com.example.smartwaterapi.config.WechatConfig;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.ArrayList;

/**
 * 闹钟定时任务调度器
 */
@Slf4j
@Component
@EnableScheduling
public class AlarmScheduler {

    @Autowired
    private AlarmService alarmService;
    
    @Autowired
    private UserService userService;
    
    @Autowired
    private DeviceService deviceService;
    
    @Autowired
    private TemplateMessageService templateMessageService;
    
    @Autowired
    private WechatConfig wechatConfig;

    /**
     * 每分钟检查一次需要触发的闹钟
     */
    @Scheduled(cron = "0 * * * * ?")
    public void checkAlarms() {
        try {
            log.debug("开始检查需要触发的闹钟...");
            
            // 获取需要触发的闹钟列表
            List<Alarm> alarmsToTrigger = alarmService.getAlarmsToTrigger();
            
            if (alarmsToTrigger.isEmpty()) {
                log.debug("当前时间没有需要触发的闹钟");
                return;
            }
            
            log.info("发现{}个闹钟需要触发", alarmsToTrigger.size());
            
            // 处理每个需要触发的闹钟
            for (Alarm alarm : alarmsToTrigger) {
                processAlarm(alarm);
            }
            
        } catch (Exception e) {
            log.error("检查闹钟异常", e);
        }
    }
    
    /**
     * 每小时清理一次孤儿闹钟（设备已删除但闹钟仍存在）
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void cleanupOrphanAlarms() {
        try {
            log.debug("开始清理孤儿闹钟...");
            int cleanedCount = alarmService.cleanupOrphanAlarms();
            if (cleanedCount > 0) {
                log.info("清理了{}个孤儿闹钟", cleanedCount);
            }
        } catch (Exception e) {
            log.error("清理孤儿闹钟异常", e);
        }
    }

    /**
     * 处理单个闹钟
     */
    private void processAlarm(Alarm alarm) {
        try {
            log.info("处理闹钟: id={}, name={}, time={}:{}, userId={}, deviceId={}", 
                alarm.getId(), alarm.getName(), alarm.getHour(), alarm.getMinute(), alarm.getUserId(), alarm.getDeviceId());
            
            // 获取用户信息
            User user = userService.getById(alarm.getUserId());
            if (user == null) {
                log.warn("用户不存在: userId={}", alarm.getUserId());
                return;
            }
            
            // 检查设备是否存在
            Device device = deviceService.getById(alarm.getDeviceId());
            if (device == null) {
                log.warn("设备不存在，跳过闹钟触发: alarmId={}, deviceId={}, userId={}", 
                    alarm.getId(), alarm.getDeviceId(), alarm.getUserId());
                return;
            }
            
            // 检查设备是否属于该用户
            if (!device.getUserId().equals(alarm.getUserId())) {
                log.warn("设备不属于该用户，跳过闹钟触发: alarmId={}, deviceId={}, deviceUserId={}, alarmUserId={}", 
                    alarm.getId(), alarm.getDeviceId(), device.getUserId(), alarm.getUserId());
                return;
            }
            
            // 检查用户是否绑定微信公众号
            if (!StringUtils.hasText(user.getWxOpenid())) {
                log.warn("用户未绑定微信公众号: userId={}", alarm.getUserId());
                return;
            }
            
            // 发送闹钟提醒模板消息
            sendAlarmTemplateMessage(alarm, user, device);
            
            // 更新闹钟下次触发时间
            updateAlarmNextTriggerTime(alarm);
            
        } catch (Exception e) {
            log.error("处理闹钟异常: alarmId={}", alarm.getId(), e);
        }
    }

    /**
     * 发送闹钟提醒模板消息
     */
    private void sendAlarmTemplateMessage(Alarm alarm, User user, Device device) {
        try {
            String currentTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
            
            // 构建模板数据
            List<WxMpTemplateData> templateData = new ArrayList<>();
            templateData.add(new WxMpTemplateData("first", "亲爱的用户，闹钟提醒时间到了！", "#FF6B6B"));
            templateData.add(new WxMpTemplateData("thing8", "喝水提醒", "#FF6B6B")); // 事件类型
            templateData.add(new WxMpTemplateData("thing23", "智能水杯", "#333333")); // 设备名称
            templateData.add(new WxMpTemplateData("time51", currentTime, "#333333")); // 发生时间
            templateData.add(new WxMpTemplateData("remark", "闹钟：" + alarm.getName() + "，请及时处理您的提醒事项。", "#306ca5"));
            
            // 构建小程序跳转信息
            WxMpTemplateMessage.MiniProgram miniProgram = new WxMpTemplateMessage.MiniProgram();
            miniProgram.setAppid(wechatConfig.getMiniAppId()); // 小程序的AppID，不是公众号的
            miniProgram.setPagePath("pages/index/index");
            
            WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
                    .toUser(user.getWxOpenid())
                    .templateId(wechatConfig.getTemplateId())
                    .miniProgram(miniProgram) // 使用小程序跳转
                    // 移除错误的URL跳转
                    .data(templateData)
                    .build();
            
            // 异步发送消息
            templateMessageService.sendTemplateMessage(templateMessage, wechatConfig.getAppid());
            
            log.info("闹钟提醒模板消息发送成功: userId={}, alarmName={}, wxOpenid={}", 
                user.getId(), alarm.getName(), user.getWxOpenid());
                
        } catch (Exception e) {
            log.error("发送闹钟提醒模板消息失败: alarmId={}, userId={}", alarm.getId(), user.getId(), e);
        }
    }

    /**
     * 更新闹钟下次触发时间
     */
    private void updateAlarmNextTriggerTime(Alarm alarm) {
        try {
            // 根据重复模式计算下次触发时间
            LocalDateTime nextTriggerTime = calculateNextTriggerTime(alarm);
            
            // 如果是单次闹钟（repeatMode = 0），触发后应该禁用
            if (alarm.getRepeatMode() == 0) {
                log.info("单次闹钟触发完成，禁用闹钟: alarmId={}, name={}", 
                    alarm.getId(), alarm.getName());
                
                // 禁用单次闹钟
                Alarm updateAlarm = new Alarm();
                updateAlarm.setId(alarm.getId());
                updateAlarm.setIsEnabled(false);
                updateAlarm.setNextTriggerTime(null);
                updateAlarm.setUpdateTime(LocalDateTime.now());
                
                boolean success = alarmService.updateById(updateAlarm);
                if (success) {
                    log.info("单次闹钟禁用成功: alarmId={}", alarm.getId());
                } else {
                    log.warn("单次闹钟禁用失败: alarmId={}", alarm.getId());
                }
                return;
            }
            
            // 更新重复闹钟的下次触发时间
            boolean success = alarmService.updateNextTriggerTime(alarm.getId(), nextTriggerTime);
            
            if (success) {
                log.info("闹钟下次触发时间更新成功: alarmId={}, nextTriggerTime={}", 
                    alarm.getId(), nextTriggerTime);
            } else {
                log.warn("闹钟下次触发时间更新失败: alarmId={}", alarm.getId());
            }
            
        } catch (Exception e) {
            log.error("更新闹钟下次触发时间异常: alarmId={}", alarm.getId(), e);
        }
    }

    /**
     * 计算闹钟下次触发时间
     */
    private LocalDateTime calculateNextTriggerTime(Alarm alarm) {
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime today = now.toLocalDate().atTime(alarm.getHour(), alarm.getMinute());
        
        // 如果今天的时间已经过了，计算下次触发时间
        if (today.isBefore(now) || today.equals(now)) {
            return calculateNextTriggerTimeByRepeatMode(alarm, today.plusDays(1));
                    }
        
        // 如果今天的时间还没到，直接返回今天的时间
        return today;
    }
    
    /**
     * 根据重复模式计算下次触发时间
     * 兼容前端的重复模式格式：
     * - 0: 单次（不重复）
     * - 1: 每天重复
     * - 按位操作：例如 128 表示周日（二进制 10000000）
     */
    private LocalDateTime calculateNextTriggerTimeByRepeatMode(Alarm alarm, LocalDateTime fromDate) {
        int repeatMode = alarm.getRepeatMode();
        
        // 单次闹钟，触发后不再重复
        if (repeatMode == 0) {
            return null;
        }
        
        // 每天重复
        if (repeatMode == 1) {
            return fromDate;
        }
        
        // 工作日重复 (周一到周五，前端编码：位1-5 = 2+4+8+16+32 = 62)
        if (repeatMode == 62) {
            return calculateNextWorkday(fromDate);
        }
        
        // 周末重复 (周六周日，前端编码：位6-7 = 64+128 = 192)
        if (repeatMode == 192) {
            return calculateNextWeekend(fromDate);
        }
        
        // 自定义星期重复（按位操作）
        return calculateNextWeekdayByBitMask(fromDate, repeatMode);
    }
    
    /**
     * 根据位掩码计算下次触发的星期几
     * 前端位掩码格式（实际）：
     * 位1: 周一, 位2: 周二, 位3: 周三, 位4: 周四, 位5: 周五, 位6: 周六, 位7: 周日
     * （前端使用 index+1，所以从位1开始）
     */
    private LocalDateTime calculateNextWeekdayByBitMask(LocalDateTime fromDate, int repeatMode) {
        LocalDateTime next = fromDate;
        
        // 最多循环7天，防止无限循环
        for (int i = 0; i < 7; i++) {
            int dayOfWeek = next.getDayOfWeek().getValue(); // 1=周一, 7=周日
            
            // 前端位位置：周一=位1, 周二=位2, ..., 周日=位7
            int bitPosition = (dayOfWeek == 7) ? 7 : dayOfWeek; // 周日是位7，其他直接用dayOfWeek
            
            // 检查这一天是否在重复模式中
            if (((repeatMode >> bitPosition) & 1) == 1) {
                log.debug("找到匹配的重复日期: 星期{}, repeatMode={}, bitPosition={}", 
                    dayOfWeek, repeatMode, bitPosition);
                return next;
            }
            
            next = next.plusDays(1);
        }
        
        // 如果没有找到匹配的日期，返回原日期（兜底处理）
        log.warn("未找到匹配的重复日期，repeatMode={}, 返回原日期", repeatMode);
        return fromDate;
    }

    /**
     * 计算下一个工作日
     */
    private LocalDateTime calculateNextWorkday(LocalDateTime date) {
        LocalDateTime next = date;
        while (next.getDayOfWeek().getValue() > 5) { // 周六=6, 周日=7
            next = next.plusDays(1);
        }
        return next;
    }

    /**
     * 计算下一个周末
     */
    private LocalDateTime calculateNextWeekend(LocalDateTime date) {
        LocalDateTime next = date;
        while (next.getDayOfWeek().getValue() <= 5) { // 周一到周五
            next = next.plusDays(1);
        }
        return next;
    }
} 